#include "videodecodetask.h"
#include "libavinclude.h"
#include "synimagehandler.h"
VideoDecodeTask::VideoDecodeTask():
        m_pVStream(0),
        video_clock(0),
        img_convert_ctx(0),
        pFrameRGB24(0),
        bufferRGB(0),
        syn_image_handler(0),
        bExit(true)
{

}

VideoDecodeTask::~VideoDecodeTask()
{

}

void VideoDecodeTask::checkout_swsconvert()
{
    if(img_convert_ctx == 0)
    {
        int width   = m_pVStream->codec->width;
        int height  = m_pVStream->codec->height;

        img_convert_ctx = sws_getContext(width,height ,m_pVStream->codec->pix_fmt,width,height,//AV_PIX_FMT_YUV420P
                                         AV_PIX_FMT_RGB32 , SWS_FAST_BILINEAR, NULL, NULL, NULL);

         int numBytesRGB = avpicture_get_size(AV_PIX_FMT_RGB32, width,height);

         bufferRGB = (uint8_t *)malloc(numBytesRGB * sizeof(uint8_t));

         pFrameRGB24 = avcodec_alloc_frame();

         avpicture_fill((AVPicture *)pFrameRGB24, bufferRGB, AV_PIX_FMT_RGB32,width , height);
    }
}

bool VideoDecodeTask::Init(AVStream *stream, SynImagehandler *handler)
{
    if(stream==0 || stream->codec==0)
        return false;

    AVCodec  * pCodecV = avcodec_find_decoder(stream->codec->codec_id);
    if ((pCodecV ==0) || (avcodec_open2(stream->codec, pCodecV,NULL) < 0))
        return false;

    m_pVStream          = stream;
    syn_image_handler   = handler;
    video_clock         = 0;
    skipped_frame       = 0;
     return true;
}

void VideoDecodeTask::UnInit()
{
    if(m_pVStream && m_pVStream->codec)
    {
        avcodec_flush_buffers(m_pVStream->codec);

        av_freep(&m_pVStream->codec->extradata);
        m_pVStream->codec->extradata_size = 0;
        avcodec_close(m_pVStream->codec);
    }
    if(pFrameRGB24)
    {
        av_free(pFrameRGB24);
        pFrameRGB24 = 0;
    }

    if(img_convert_ctx)
    {
        sws_freeContext(img_convert_ctx);
        img_convert_ctx = 0;
    }

    if(bufferRGB)
    {
        free(bufferRGB);
        bufferRGB = 0;
    }
    syn_image_handler   = 0;
    m_pVStream          =  0;
}

int VideoDecodeTask::close()
{
    bExit = true;
    std::string str("");
    int nthread  =thr_count();

    for (int i =0 ;i<nthread ;i++)
        putq(str);

    wait();
    return 0;
}

bool VideoDecodeTask::receive_handler(const char *data, int size)
{
    putq(std::string(data,size));
    return true;
}
double VideoDecodeTask::synchronize_video(AVFrame * src_frame, double pts)
{
    double frame_delay;

    if (pts != 0)
    {
        video_clock = pts;
    }
    else
    {
        pts = video_clock;
    }
    // update the video clock
    frame_delay = av_q2d(m_pVStream->codec->time_base);
    // if we are repeating a frame, adjust clock accordingly
    frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);

    video_clock += frame_delay;

    return pts;
}

bool VideoDecodeTask::has_data_decode(const char *data, int len,AVFrame * pFrame,unsigned char *pBuffer)
{
    double pts;

    AVPacket pktl;

    memset(&pktl,0,sizeof(AVPacket));

    int frameFinished = 0;

    memcpy(&pktl,data,len);  //取出数据

    int len1 = avcodec_decode_video2(m_pVStream->codec, pFrame, &frameFinished,&pktl);

    int width   = m_pVStream->codec->width;
    int height  = m_pVStream->codec->height;

    if (frameFinished)
    {
        pts = av_frame_get_best_effort_timestamp(pFrame);

        if (pts == AV_NOPTS_VALUE)
        {
            pts = 0;
        }

        pts *= av_q2d(m_pVStream->time_base);

        pts = synchronize_video(pFrame, pts);
        //先不判断长度
        checkout_swsconvert();
        sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, height, pFrameRGB24->data, pFrameRGB24->linesize);

        memset(pBuffer,0,width*height*4);
        memcpy(pBuffer,pFrameRGB24->data[0],pFrameRGB24->linesize[0]*height);

        if(syn_image_handler)
        {
             //qDebug(QString("pts  :").append(QString::number((long)pts)).toLocal8Bit().data());
             syn_image_handler->syn_image_handler(pBuffer,pFrameRGB24->linesize[0]*m_pVStream->codec->height,pts,width,height);
        }
        return true;
    }else
    {
        skipped_frame ++;
        return false;
    }
}

bool VideoDecodeTask::no_data_decode(AVFrame * pFrame,unsigned char *pBuffer)
{
    double pts;

    AVPacket pktl;

    memset(&pktl,0,sizeof(AVPacket));

    int frameFinished = 0;

    int len1 = avcodec_decode_video2(m_pVStream->codec, pFrame, &frameFinished,&pktl);


    int width   = m_pVStream->codec->width;
    int height  = m_pVStream->codec->height;

    if (frameFinished)
    {
        pts = av_frame_get_best_effort_timestamp(pFrame);

        if (pts == AV_NOPTS_VALUE)
        {
            pts = 0;
        }

        pts *= av_q2d(m_pVStream->time_base);

        pts = synchronize_video(pFrame, pts);
        //先不判断长度
        checkout_swsconvert();
        sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, height, pFrameRGB24->data, pFrameRGB24->linesize);

        memset(pBuffer,0,width*height*4);

        memcpy(pBuffer,pFrameRGB24->data[0],pFrameRGB24->linesize[0]*height);

        if(syn_image_handler)
        {
             //qDebug(QString("pts  :").append(QString::number((long)pts)).toLocal8Bit().data());
             syn_image_handler->syn_image_handler(pBuffer,pFrameRGB24->linesize[0]*m_pVStream->codec->height,pts,width,height);
        }
        skipped_frame --;

        return true;
    }else
    {
        return false;
    }
}


int VideoDecodeTask::svc()
{
    bExit = false;

    AVFrame * pFrame = avcodec_alloc_frame();

    AVPacket pktl;

    memset(&pktl,0,sizeof(AVPacket));

    std::string str ="";

    int width   = 1920;
    int height  = 1080;

    unsigned char *pBuffer = new unsigned char[width*height*4];

    while(getq(str,!bExit))
    {
        if(str=="")
            break;

	memset(&pktl, 0, sizeof(AVPacket));
        memcpy(&pktl, str.data(), str.length());

        if(skipped_frame > 0)
        {
            while(no_data_decode(pFrame,pBuffer) &&(skipped_frame > 0)){}
        }

        has_data_decode(str.data(),str.length(),pFrame,pBuffer);
        av_free_packet(&pktl);
    }
    avcodec_free_frame(&pFrame);

    delete []pBuffer;

    return 1;
}

